package analizadorSintactico;

import java.util.ArrayList;
import java.util.Iterator;

import analizadorLexico.Token;


public class Sintactico {

	private ArrayList<Token> tokens;	//Tokens que le llegan del Léxico
	private Integer indice;	//Para llevar la cuenta de los tokens leidos
	private ArrayList<String> cod;	//Se encarga de ir guardando las instrucciones del codigo de la maquina a pila
	private TablaSimbolos ts;	//Tabla de símbolos
	private ArrayList<String> tablaErrores;
	
	public Sintactico(ArrayList<Token> tokens){
		this.tokens = tokens;
		cod=new ArrayList<String>();
		indice = 0;
		ts=null;
		tablaErrores = new ArrayList<String>();
	}
		
	public Boolean analiza() throws ErrorSintacticoExcepcion{
		
		Boolean error = false;
		prog(error);
		
		/*Para hacerlo por consola
		if (!error && tablaErrores.isEmpty()){
			System.out.println("No ha habido errores en el analizador sintáctico");
			//Creo fichero para maquina
			
		}
		else if (!tablaErrores.isEmpty()){
			Iterator<String> it = tablaErrores.iterator();
			String mensaje = "";
			
			while (it.hasNext()){
				mensaje = it.next();
				System.out.println(mensaje);
			}	
		}
		*/
		return !error;
	}
	
	//---------------------------------
	//----    Getters / Setters    ----
	//---------------------------------
	public ArrayList<String> getCod() {
		return cod;
	}
	
	public ArrayList<String> getTablaErrores() {
		return tablaErrores;
	}
	
	public TablaSimbolos getTS(){
		return ts;
	}
	/*
	 *Prog (out  err0) ::= program : identificador { vars-consts { 
		DecsMain (out err1)
			{
			}
 		} instructions { 
		InstsMain (out err2)
			{
				err0 <-err1 v err2;
				emite(stop)
			}
	}}
 
	 * */
	private void prog(Boolean err0) throws ErrorSintacticoExcepcion{
	
		Boolean err1 = false,err2 = false;
		
		//Compruebo que el primer token sea "program"
		Token tprog=cogerToken();
		compruebaValor("program",": se esperaba la palabra reservada 'program'",tprog);
		//Avanzo token
		indice++;
		
		//Compruebo que el siguiente token sea ":"
		tprog=cogerToken();
		compruebaValor(":",": se esperaba ':'",tprog);
		//Avanzo token
		indice++;
		
		//Compruebo que el siguiente token sea un identificador
		tprog=cogerToken();
		compruebaTipo(Token.Tipo.identificador,": se esperaba un identificador",tprog);
		//Avanzo token
		indice++;
		
		//Compruebo que el siguiente token sea "{"
		tprog=cogerToken();
		compruebaValor("{",": se esperaba '{'",tprog);
		//Avanzo token
		indice++;
		
		//Compruebo que el primer token sea "vars-consts"
		tprog=cogerToken();
		compruebaValor("vars-consts",": se esperaba la palabra reservada 'vars-consts'",tprog);
		//Avanzo token
		indice++;
		
		//Compruebo que el siguiente token sea "{"
		tprog=cogerToken();
		compruebaValor("{",": se esperaba '{'",tprog);
		//Avanzo token
		indice++;	
		
		DecsMain(err1);
		
		//Compruebo que el siguiente token sea "}"
		tprog=cogerToken();
		compruebaValor("}",": se esperaba '}'",tprog);
		//Avanzo token
		indice++;	
		
		//Compruebo que el primer token sea "instructions"
		tprog=cogerToken();
		compruebaValor("instructions",": se esperaba la palabra reservada 'instructions'",tprog);
		//Avanzo token
		indice++;	
	
		//Compruebo que el siguiente token sea "{"
		tprog=cogerToken();
		compruebaValor("{",": se esperaba '{'",tprog);
		//Avanzo token
		indice++;
		
		InstsMain(err2);
		
		//Se mira si ha habido errores y finaliza el programa		
		err0 = err1 || err2;
		emite("stop");
		
		//Compruebo que el siguiente token sea "}"
		tprog=cogerToken();
		compruebaValor("}",": se esperaba '}'",tprog);
		//Avanzo token
		indice++;
				
		//Compruebo que el siguiente token sea "}"
		tprog=cogerToken();
		compruebaValor("}",": se esperaba '}'",tprog);
		//Avanzo token
		indice++;
	}
	
	
	/*
	 * DECLARACIONES *************************************************************************************************
	 * 
	 * */
	
	
	
	/*
	 *  DecsMain ( out err0 ) ::= 
			Decs ( out err1 )
				{
					err0 <- err1
				}
		DecsMain ( out err0) ::= λ
			{
				ts <- creaTablaSimbolos();
				err0 <- false;
			}
	 * */
	private void DecsMain(Boolean err0) throws ErrorSintacticoExcepcion{
		Token tprog=cogerToken();
		
		//Miro a ver si hay instrucciones o no, ya que puede estar vacío:
		//Si está vacío
		if (tprog.getValor().equals("}")){
			ts = creaTablaSimbolos();
			err0 = false;
		}
		//Si no está vacío
		else{
			Boolean err1 = false;

			Decs(err1);
			err0 = err1;
		}
		
	}
	
	/*
	 * Decs(out err0) ::= 
		{
			dirh1 <- 0
		}
		
		Dec(in dirh1; out err1, id1, tipo1, const1) 
			{
				dirh2 <- 1;
				ts <- agregaId( creaTablaSimbolos(), id1, 0, tipo1, const1 );
				errh2 <- err1;
			}
		
		RDecs(in dirh2, errh2; out err2)
			{
				err0 <- err2;
			}
	 * */
	private void Decs(Boolean err0) throws ErrorSintacticoExcepcion{
		
		Boolean err1 = false, err2 = false, errh2 = false;
		Cadena id1= new Cadena();
		Integer dirh1, dirh2;
		Cadena const1 = new Cadena();
		Token.Tipo tipo1 = null;
		
		dirh1 = 0;
		tipo1 = Dec(dirh1,id1,const1,err1);
				
		dirh2 = 1;
		ts = creaTablaSimbolos();
		ts.agregaId(id1.getContenido(), 0, tipo1, const1);
		errh2 = err1;
		
		RDecs(dirh2, errh2, err2);
		
		err0 = err2;
	}
	
	/*
	  RDecs (in dirh0, errh0; out err0) ::= 
			{
				dirh1 <- dirh0
			}
			Dec (in dirh1; out id1, tipo1, const1, err1)
				{
					dirh2 <- dirh0 + 1;
					ts <- agregaId(id1, dirh0, tipo1, const1 );
					errh2 <- errh0 v err1 v existeId(ts,id1);
				}
				
			RDecs (in dirh2, errh2 ; out err2)
				{
					err0 <- err2;
				}
			
		RDecs ( in dirh0, errh0; out err0) ::=
			{
			err0 <- errh0;
			}

	 * */
	private void RDecs(Integer dirh0, Boolean errh0, Boolean err0) throws ErrorSintacticoExcepcion{

		//Si hay dec
		Token tprog=cogerToken();
		if (!tprog.getValor().equals("}")){
			Boolean err1 = false, err2 = false, errh2 = false, error = false;
			Integer dirh1, dirh2;
			Cadena const1 = new Cadena();
			Token.Tipo tipo1 = null;
			Cadena id1 = new Cadena();
			
			dirh1 = dirh0;
			
			tipo1 = Dec(dirh1,id1,const1,err1);
			
			//avanzo indice
			//indice++;
			
			
			dirh2 = dirh0 + 1;
			if (ts.existeId(id1.getContenido())){
				error=true;
				tablaErrores.add("Error en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": "+tprog.getValor()+" ya ha sido declarado anteriormente.");
			}
			errh2 = errh0 || err1 || error;
			ts.agregaId(id1.getContenido(), dirh0, tipo1, const1);
			
			//Llamada recursiva a RDecs
			RDecs(dirh2, errh2,err2);
			err0 = err2;
				
			
		}
		//Si es vacío
		else{
			err0 = errh0;
		}
		
		
		
	}
	
	/*
	 * Dec (in dirh0; out id0, tipo0, const0, err0 ) ::= var Tipo 
			Tipo (out valor1)
				{		 
					id0 <- identificador.lex;
					tipo0 <- valor1;
					const0 <- 0;
					err0 <- false;
				}
			identificador ;
		
		Dec (in dirh0; out id0,tipo0, const0, err0)::= const 
			Tipo (out valor1)
					{ 
						id0 <- identificador.lex;
						tipo0 <- valor1;
						const0 <- 1;
					}
			identificador := 
			Signo(out op2, tipo2)
			Valor(out tipo3,valor3) 
				{ 
					err0 <- errorAsignacionTipos(tipo0 , tipo3, tipo2);
					emite(cambiaTipo(valor1));
					emite(apila(valor3));
					emite(op2);
					emite(desapilaVar(dirh0))
				}
			;
	
	 * */
	private Token.Tipo Dec(Integer dirh0,Cadena id0,Cadena const0,Boolean err0) throws ErrorSintacticoExcepcion{
		
		Token.Tipo tipo0 = null;
		Cadena valor1 = new Cadena();
		Token tprog=cogerToken();
		//Avanzo token
		indice++;
				
		//Si es la declaración de una variable
		if (tprog.getValor().equals("var")){
			
			//Compruebo que el primer token sea un Tipo
			tprog=cogerToken();
			if (!esTipoDec(tprog)){
				throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
										", palabra "+tprog.getColumna()+ ": se esperaba un Tipo");
			}

			tipo(valor1);
			//Avanzo token
			indice++;
			tprog=cogerToken();

			id0.setContenido(tprog.getValor());
			tipo0 = convierteValorATipo(valor1);//tipo0 = valor1;
			const0.setContenido("false");
			err0 = false;
		
			
			//Compruebo que el siguiente token sea un identificador
			compruebaTipo(Token.Tipo.identificador,": se esperaba un identificador",tprog);
			//Avanzo token
			indice++;
			
			//Compruebo que el siguiente token sea ";"
			tprog=cogerToken();
			compruebaValor(";",": se esperaba ';'",tprog);
			//Avanzo token
			indice++;			
		}
		//Si es la declaración de una constante
		else if (tprog.getValor().equals("const")){
			
			//String op2 = "", operacion2 = "", valor3 = "";
			Cadena op2 = new Cadena(), valor3 = new Cadena();
			Token.Tipo tipo2,tipo3;
			
			//Compruebo que el primer token sea un Tipo
			tprog=cogerToken();
			if (!esTipoDec(tprog)){
				throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
										", palabra "+tprog.getColumna()+ ": se esperaba un Tipo");
			}

			tipo(valor1);
			//Avanzo token
			indice++;
			tprog = cogerToken();
			
			id0.setContenido(tprog.getValor());
			tipo0 = convierteValorATipo(valor1);//tipo0 = valor1;
			const0.setContenido("true");

			tprog=cogerToken();
			//Compruebo que el siguiente token sea un identificador
			compruebaTipo(Token.Tipo.identificador,": se esperaba un identificador",tprog);
			//Avanzo token
			indice++;
			
			//Compruebo que el siguiente token sea ":="
			tprog=cogerToken();
			compruebaValor(":=",": se esperaba ':='",tprog);
			//Avanzo token
			indice++;

			tipo2 = signo(op2);
			tipo3 = valor(valor3);
			
			err0 = errorAsignacionTipos(tipo0 , tipo3, tipo2);
		
			if (err0){
				throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+": Error en la asignación de tipos en la constante");
			}
			
			emite("cambiaTipo("+tipo0+")");
			emite("apila("+valor3.getContenido()+")");
			emite(op2.getContenido());
			emite("desapilaVar("+dirh0+")");
			
			//Avanzo índice
			indice++;
			//Compruebo que el siguiente token sea ";"
			tprog=cogerToken();
			compruebaValor(";",": se esperaba ';'",tprog);
			//Avanzo token
			indice++;	
		}
		else throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
				", palabra "+tprog.getColumna()+ ": se esperaba 'var' o 'const'");
		
		return tipo0;
	}
	
	/*
	 * 	Signo( out op0, tipo0) ::=
			{
				tipo0  = λ
				op0 = λ
			}
		Signo( out op0, tipo0) ::= +	
			{
				tipo0 = sum.lex
				op0 = λ
			}
		Signo( out op0, tipo0) ::= -
			{
				tipo0 = rest.lex
				op0 = menosUnario
			}

	 * */
	private Token.Tipo signo(Cadena op0) throws ErrorSintacticoExcepcion{
		
		Token.Tipo tipo0 = null;
		
		//Compruebo si el siguiente token sea + 
		Token tprog=cogerToken();
		if (tprog.getValor().equals("+")){
			tipo0 = tprog.getTipo();
			op0.setContenido("");
			
			//Avanzo token
			indice++;
		}
		//Compruebo si el siguiente token sea -
		else if(tprog.getValor().equals("-")){
			tipo0 = tprog.getTipo();
			op0.setContenido("menosUnario");
			
			//Avanzo token
			indice++;
		}
		else if (!esValorDec(tprog)){
			throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+": Se espera +,- ó valor");

		}
				
		return tipo0;
	}
	
	/*
	 * Tipo(out valor0) ::= float
		{ 
			valor0 <- float.lex 
		}
		
		Tipo(out valor0) ::= integer
		{ 
			valor0 <- integer.lex 
		}
		
		Tipo(out valor0) ::= natural
		{ 
			valor0 <- natural.lex 
		}
		
		Tipo(out valor0) ::= character
		{ 
			valor0 <- character.lex 
		}
		
		Tipo(out valor0) ::= boolean
		{ 
			valor0 <- boolean.lex 
		}
	
	 * */
	private void tipo(Cadena valor0) throws ErrorSintacticoExcepcion{

		Token tprog=cogerToken();
		String valor = tprog.getValor();
		
		//Se compara con el valor, ya que el valor es el String (En este caso float) 
		//y el tipo es identificador
		if (valor.equals("float")){
			valor0.setContenido("float");
		}
		else if(valor.equals("integer")){
			valor0.setContenido("integer");
		}
		else if(valor.equals("natural")){
			valor0.setContenido("natural");
		}
		else if(valor.equals("character")){
			valor0.setContenido("character");
		}
		else if(valor.equals("boolean")){
			valor0.setContenido("boolean");
		}
		else {
			throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+ ": se esperaba un tipo.");	
		}
		
	}
	
	/*
	 * 
		Valor(out tipo0, valor0) ::= real
			{ 
				tipo0 <-float.lex;
				valor0 <-real.lex 
			}
		
		Valor(out tipo0, valor0) ::= natural
			{ 
				tipo0 <-natural.lex;
				valor0 <-natural.lex 
			}
		
		Valor(out tipo0, valor0) ::= caracter
			{ 
				tipo0 <-character.lex;
				valor0 <-caracter.lex 
			}
		
		Valor(out tipo0, valor0) ::= true
			{
		 		tipo0 <-boolean.lex;
				valor0 <-true.lex 
			}
		
		Valor(out tipo0, valor0) ::= false
			{ 
				tipo0 <- boolean.lex;
				valor0 <- false.lex
			}

	 * */
	private Token.Tipo valor(Cadena valor0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor=tprog.getValor();
		Token.Tipo tipo = tprog.getTipo(), tipo0 = null;
		
		if(tipo.equals(Token.Tipo.real)) {
			tipo0 = Token.Tipo.real;
			valor0.setContenido(valor);
		}
		else if(tipo.equals(Token.Tipo.natural)) {
			tipo0 = Token.Tipo.natural;
			valor0.setContenido(valor);
		}
		else if(tipo.equals(Token.Tipo.caracter)) {
			tipo0 = Token.Tipo.caracter;
			valor0.setContenido(valor);
		}
		else if(tipo.equals(Token.Tipo.booleano)) {
			tipo0 = Token.Tipo.booleano;
			valor0.setContenido(valor);
		}
		else{
			throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": se esperaba '+','-' o un valor");
		} 
			
		return tipo0;
	}
	

	
	/*
	 *     INSTRUCCIONES *************************************************************************************************    
	 * */
	
	
	
	/*
	 * 	InstsMain (out err0) ::= λ
			{
				err0 = false;
			}
			
		InstsMain ( out err0) ::= 
			{}
			Insts (out err1)
				{
					err0 = err1
				}

	 * */
	private void InstsMain(Boolean err0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		
		//Si están vacías las instrucciones
		if (tprog.getValor().equals("}")){
			err0 = false;
		}
		//Si hay instrucciones
		else{
			Boolean err1 = false;
			
			Insts(err1);
			err0 = err1;
		}
	}
	
	/*
	* Insts (out err0) ::=
		{
		}
		Inst (out err1)
			{
				errh2 = err1;
			}
		Rinsts ( in errh2; out err2)
			{
				err0 = err2
			}

	 * */
	private void Insts(Boolean err0) throws ErrorSintacticoExcepcion{
		
		Boolean err1 = false, err2 = false, errh2 = false;
		
		Inst(err1);
		errh2 = err1;
			
		RInsts(errh2, err2);
		err0 = err2;
			
	}
	
	/*
	 * 	RInsts (in errh0; out err0) ::=
			{}
			Inst (out err1)
				{
					errh2 = errh0 v err1;
				}
			RInsts (in errh2; out err2) 
				{
					err0 = err2
				}
		
		RInsts (in errh0; out err0) ::=
			{
				err0 = errh0
			}

	 * */
	private void RInsts(Boolean errh0, Boolean err0) throws ErrorSintacticoExcepcion{
		
		//Si hay instruccion
		Token tprog=cogerToken();
		if (!tprog.getValor().equals("}")){
			Boolean err1 = false, err2 = false, errh2 = false;
			
			Insts(err1);
			errh2 = err0 || err1;
			
			RInsts(errh2, err2);
			err0 = err2;
		}
		//Si es vacio
		else{
			err0 = errh0;
		}
	}
	
	/*
	 * Inst (out err0) ::= identificador 
	 * 		=
			{
				
			} 
			Exp0 (out tipo1)
			{
				err0 = !existeId( ts, identificador.lex ) v esConstante(ts, identificador.lex) v ( tipo1 == error.lex ) v 
				errorAsignacionTipos( dameTipo( ts, identificador.id ), tipo1, λ );
				emite(cambiaTipo(dameTipo(ts, identificador.lex)))
				emite(desapilaVar ( dameDireccion( ts, identificador.lex ) ))
			}
			;
			
		Inst ( out err0) ::= swap1 ( ) ;
			{
				err0 = false;
				emite(swap1Func)
			}
			
		Inst ( out err0) ::= swap2 ( ) ;
			{
				err0 = false;
				emite( swap2Func)
			}
			
		Inst (out err0) ::= in ( identificador ) ;
			{
				err0 = !existeId( ts, identificador.lex ) v 
				esConstante( ts, identificador.lex );
				emite( cambiaTipo( dameTipo(tsh0, identificador.lex)));
				emite(inRead(dameDireccion( ts, identificador.lex )))
			}
			
		Inst (out err0) ::= out (
			{} 
			Exp0 (out tipo1)
				{
					err0 = ( tipo1 == error.lex );
					emite(cambiaTipo( tipo1 ));
					emite(outPrint)
				}
			) 
			;

	 * */
	private void Inst(Boolean err0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		String id = "";
		boolean errAsig = false;
		
		//Si es un identificador
		if (tprog.getTipo() == null){
			throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+": Debe haber un identificador, out, in swap1, swap2");
		}
		else if (tprog.getTipo().equals(Token.Tipo.identificador)){
			Token.Tipo tipo1 = null;
			Boolean error = false, errorConst = false;
						
			id = tprog.getValor();
			Token idTok=tprog;
			//Avanzo token
			indice++;	
		
			//Compruebo que el siguiente token sea "="
			tprog=cogerToken();
			compruebaValor("=",": se esperaba '='",tprog);
			//Avanzo token
			indice++;
			
			
			
			tipo1 = Exp0();
			tprog = cogerToken();
			
			//Comprueba si hay no se ha declarado anteriormente
			if (!ts.existeId(id)){
				error = true;
				tablaErrores.add("Error en la línea  "+idTok.getFila()+
				", palabra "+idTok.getColumna()+ ": "+ "el identificador "+id+" no ha sido declarado anteriormente.");
				
			}
			
			//Comprueba si es constante
			else if (ts.esConstante(id)){
				errorConst = true;
				tablaErrores.add("Error: el identificador '"+id+"' no se puede modificar, es una constante.");
			}
			errAsig = errorAsignacionTipos(ts.dameTipo(id), tipo1, null);
			err0 = error || errorConst || tipo1.equals("error") || errAsig;
			if (errAsig && (!ts.dameTipo(id).equals(Token.Tipo.error)) && (!tipo1.equals(Token.Tipo.error)))
				tablaErrores.add("Error: en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": el tipo '"+ts.dameTipo(id)+"' del identificador '"+id+"' no corresponde con el tipo asignado '"+tipo1+"'.");
			
			emite("cambiaTipo("+ts.dameTipo(id)+")");
			emite("desapilaVar("+ts.dameDireccion(id)+")");
			
			//Compruebo que el siguiente token sea ";"
			tprog=cogerToken();
			compruebaValor(";",": se esperaba ';'",tprog);
			//Avanzo token
			indice++;
		}
		//Si es Swap1()
		else if (valor.equals("swap1")){
			
			//Avanzo token
			indice++;	
			
			//Compruebo que el siguiente token sea "("
			tprog=cogerToken();
			compruebaValor("(",": se esperaba '('",tprog);
			//Avanzo token
			indice++;
			
			//Compruebo que el siguiente token sea ")"
			tprog=cogerToken();
			compruebaValor(")",": se esperaba ')'",tprog);
			//Avanzo token
			indice++;
			
			err0 = false;
			emite("swap1Func");
			
			//Compruebo que el siguiente token sea ";"
			tprog=cogerToken();
			compruebaValor(";",": se esperaba ';'",tprog);
			//Avanzo token
			indice++;
		}
		//Si es Swap2()
		else if (valor.equals("swap2")){
					
			//Avanzo token
			indice++;	
			
			//Compruebo que el siguiente token sea "("
			tprog=cogerToken();
			compruebaValor("(",": se esperaba '('",tprog);
			//Avanzo token
			indice++;
			
			//Compruebo que el siguiente token sea ")"
			tprog=cogerToken();
			compruebaValor(")",": se esperaba ')'",tprog);
			//Avanzo token
			indice++;
			
			err0 = false;
			emite("swap2Func");
			
			//Compruebo que el siguiente token sea ";"
			tprog=cogerToken();
			compruebaValor(";",": se esperaba ';'",tprog);
			//Avanzo token
			indice++;
		}
		//Si es in
		else if (valor.equals("in")){
			Boolean error = false;
			
			//Avanzo token
			indice++;	
			
			//Compruebo que el siguiente token sea "("
			tprog=cogerToken();
			compruebaValor("(",": se esperaba '('",tprog);
			//Avanzo token
			indice++;
			//estaba mal actualizar token
			tprog=cogerToken();
			
			//Compruebo que el siguiente sea un identificador
			compruebaTipo(Token.Tipo.identificador,": se esperaba un identificador",tprog);
			//Avanzo token
			String id1=tprog.getValor();
			indice++;
			
			//Compruebo que el siguiente token sea ")"
			tprog=cogerToken();
			compruebaValor(")",": se esperaba ')'",tprog);
			//Avanzo token
			indice++;
			
			//Compruebo que el siguiente token sea ";"
			tprog=cogerToken();
			compruebaValor(";",": se esperaba ';'",tprog);
			//Avanzo token
			indice++;
			
			//Comprueba si hay no se ha declarado anteriormente
			if (!ts.existeId(id1)){
				error = true;
				tablaErrores.add("Error en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": "+id1+" no ha sido declarado anteriormente.");
			}
			else if (ts.esConstante(id1)) {
				error = true;
				tablaErrores.add("Error en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": "+id1+" es una constante.");
			}
			valor = id1;
			err0 = error || ts.esConstante(valor);
			emite("cambiaTipo("+ts.dameTipo(valor)+")");
			emite("inRead("+ts.dameDireccion(valor)+")");
		}
		//Si es out
		
		else if (valor.equals("out")){
			Token.Tipo tipo1 = null;
			
			//Avanzo token
			indice++;	
			
			//Compruebo que el siguiente token sea "("
			tprog=cogerToken();
			compruebaValor("(",": se esperaba '('",tprog);
			//Avanzo token
			indice++;
			
			tipo1 = Exp0();
			
			err0 = tipo1.equals("error");
			emite("cambiaTipo("+tipo1+")");
			emite("outPrint");
			
			//Compruebo que el siguiente token sea ")"
			tprog=cogerToken();
			compruebaValor(")",": se esperaba ')'",tprog);
			//Avanzo token
			indice++;
			
			//Compruebo que el siguiente token sea ";"
			tprog=cogerToken();
			compruebaValor(";",": se esperaba ';'",tprog);
			//Avanzo token
			indice++;
		}
		else if (tprog.getTipo().equals(Token.Tipo.palabra_reservada)){
			throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+": es una palabra reservada");
		}
		else{
			throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+": Debe haber un identificador, out, in swap1, swap2");
		}
		
	}
	
	/*
	 * 	Exp0 (out tipo0) ::=
			{} 
			Exp1 (out tipo1)
				{}
			FExp0 (out tipo2)
				{
					tipo0 = errorOperacion0Tipos( tipo1, tipo2 );
				}

	 * */
	private Token.Tipo Exp0() throws ErrorSintacticoExcepcion{
		
		Token.Tipo tipo0 = null;
		
		Token.Tipo tipo1 = null, tipo2 = null;
		tipo1 = Exp1();
	
		tipo2 = FExp0();
		
		tipo0 = errorOperacion0Tipos(tipo1, tipo2);
		
		//Para ver que hay un error y guardarlo en la tabla
		Token tprog = cogerToken();
		if (tipo0.equals(Token.Tipo.error) && !tipo1.equals(Token.Tipo.error) && !tipo2.equals(Token.Tipo.error))
			tablaErrores.add("Error: en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+ ": Error de operación de tipos de nivel 0 (relacionales)");
		return tipo0;
	}
	
	/*
	 * 	FExp0 (out tipo0) ::=
			Op0 ( out op1 )
				{}
			Exp1 ( out tipo1)
				{
					tipo0 = tipo1;
					emite(op1)
				}
			
		Fexp0 ( out tipo0) ::= λ
			{
				tipo0 = λ
			}

	 * */
	private Token.Tipo FExp0() throws ErrorSintacticoExcepcion{
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo0 = null;
		
		if (esOperador0(valor)){
			Cadena op1 = new Cadena();
			Token.Tipo tipo1 = null;
			
			op0(op1);
			//aumento indice
			indice++;
			tipo1 = Exp1();
			tipo0 = tipo1;
			emite(op1.getContenido());
		}
		
		return tipo0;
		
	}
	
	/*
	 * 	Op0 ( out op0 ) ::= <
			{
				op0 = menor
			}
			
		Op0 ( out op0 )::= >
			{
				op0 = mayor
			}
			
		Op0 ( out op0 ) ::= <=
			{
				op0 = menorIgual
			}
			
		Op0 ( out op0 ) ::= >=
			{
				op0 = mayorIgual
			}
			
		Op0 ( out op0 ) ::= ==
			{
				op0 = igual
			}
			
		Op0 ( out op0 ) ::= !=
			{
				op0 = distinto
			}

	 * 
	 * */
	private void op0(Cadena op0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		
		if (valor.equals("<")){
			op0.setContenido("menor");
		}
		else if (valor.equals(">")){
			op0.setContenido("mayor");
		}
		else if (valor.equals("<=")){
			op0.setContenido("menorIgual");
		}
		else if (valor.equals(">=")){
			op0.setContenido("mayorIgual");
		}
		else if (valor.equals("==")){
			op0.setContenido("igual");
		}
		else if (valor.equals("!=")){
			op0.setContenido("distinto");
		}
	}
	
	/*
	 * 	Exp1 (out tipo0) ::= 
			{}
			Exp2 (out tipo1)
				{
					tipoh2 = tipo1
				}
			Rexp1 ( in tipoh2 ; out tipo2)
				{
					tipo0 = tipo2
				}

	 * 
	 * */
	private Token.Tipo Exp1() throws ErrorSintacticoExcepcion{
		
		Token.Tipo tipo0 = null, tipo1 = null, tipo2 = null, tipoh2 = null;
		
		tipo1 = Exp2();
		tipoh2 = tipo1;
		tipo2 = RExp1(tipoh2);
		
		tipo0 = tipo2;
		
		return tipo0;
	}
	
	
	/*
	 * 	RExp1 ( in tipoh0; out tipo0) ::=
			Op1 ( out tipo1 , op1)
			{}
			Exp2 (out tipo2)
				{
					tipoh3 = errorOperacion1Tipos( tipoh0, tipo2, tipo1 )
					emite(op1)
				}
			RExp1 ( in tipoh3; out tipo3)
				{
					tipo0 = tipo3
				}
		
		RExp1 ( in tipoh0; out tipo0) ::= λ
			{
				tipo0 = tipoh0
			}

	 * */
	private Token.Tipo RExp1(Token.Tipo tipoh0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo0 = null;
		
		if (esOperador1(valor)){
			Cadena  op1 = new Cadena();
			Token.Tipo tipo2 = null,tipo3 = null, tipoh3 = null;
			Token.Tipo tipo1;
			tipo1 = op1(op1);
			
			//avanzo indice
			indice++;
			
			tipo2 = Exp2();
			tipoh3 = errorOperacion1Tipos(tipoh0, tipo2, tipo1);
			
			//Para ver que hay un error y guardarlo en la tabla
			if (tipoh3.equals(Token.Tipo.error) && !tipoh0.equals(Token.Tipo.error) && !tipo2.equals(Token.Tipo.error))
				tablaErrores.add("Error: en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": Error de operación de tipos de nivel 1 (aritmético-lógicas de menor prioridad)");
			
			emite(op1.getContenido());
			//Llamada recursiva a RExp
			tipo3 = RExp1(tipoh3);
			
			tipo0 = tipo3;
						
		}
		//Si es vacío
		else{
			tipo0 = tipoh0;
		}
		
		return tipo0;
	}
	
	/*
	 * 	Op1 ( out tipo0, op0) ::= +
			{
				tipo0 = sum.lex
				op0 = suma
			}
			
		Op1 ( out tipo0, op0) ::= -
			{
				tipo0= rest.lex
				op0 = resta
			}
			
		Op1 ( out tipo0, op0) ::= or
			{
				tipo0 = logic.lex
				op0 = orLogico
			}

	 * */
	private Token.Tipo op1(Cadena op0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo = tprog.getTipo(), tipo0 = null;
		
		if (valor.equals("+")){
			tipo0 = tipo;
			op0.setContenido("suma");
		}
		else if (valor.equals("-")){
			tipo0 = tipo;
			op0.setContenido("resta");
		}
		else if (valor.equals("or")){
			tipo0 = tipo;
			op0.setContenido("orLogico");
		}
		
		return tipo0;
	}
	
	/*
	 * 	Exp2 (out tipo0) ::= 
			{}
			Exp3 (out tipo1)
				{
					tipoh2 = tipo1
				}
			RExp2 ( in tipoh2 out tipo2)
				{
				tipo0 = tipo2
				}


	 * */
	private Token.Tipo Exp2() throws ErrorSintacticoExcepcion{
		Token.Tipo tipo0 = null, tipo2 = null, tipo1 = null, tipoh2 = null;
		
		tipo1 = Exp3();
		tipoh2 = tipo1;
		tipo2 = RExp2(tipoh2);
		tipo0 = tipo2;
		
		return tipo0;
	}
	
	/*
	 *	RExp2 ( in tipoh0; out tipo0) ::=
			Op2 ( out tipo1, op1 )
				{}
			Exp3 (out tipo2) 
				{
					tipoh3 = errorOperacion2Tipos( tipoh0, tipo2, tipo1)
					emite(op1)
				}
			RExp2 ( in tipoh3; out tipo3) 
				{
					tipo0 = tipo3
				}
			
		RExp2 ( in tipoh0; out tipo0 ) ::= λ
			{
				tipo0 = tipoh0
			}

	 * */
	private Token.Tipo RExp2(Token.Tipo tipoh0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo0 = null;
		
		if (esOperador2(valor)){
			Cadena op1 = new Cadena(); 
			Token.Tipo tipo1 = null, tipo2 = null,tipo3 = null, tipoh3 = null;
			tipo1 = op2(op1);
			
			//avanzo indice
			indice++;
			
			tipo2 = Exp3();
			tipoh3 = errorOperacion2Tipos(tipoh0, tipo2, tipo1);
			
			//Para ver que hay un error y guardarlo en la tabla
			if (tipoh3.equals(Token.Tipo.error) && !tipoh0.equals(Token.Tipo.error) && !tipo2.equals(Token.Tipo.error))
				tablaErrores.add("Error: en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": Error de operación de tipos de nivel 2 (aritméticos de mayor prioridad)");
			
			emite(op1.getContenido());
			//Llamada recursiva a RExp2
			tipo3 = RExp2(tipoh3);
			tipo0 = tipo3;
			
		}
		//si es vacío
		else{
			tipo0 = tipoh0;
		}
		
		return tipo0;
	}
	
	/*
	 * 	Op2 ( out tipo0, op0) ::= *
			{
				tipo0 = mult.lex
				op0 = multiplica
			}
			
		Op2 ( out tipo0, op0) ::= /
			{
				tipo0 = div.lex
				op0 = divide
			}
			
		Op2 ( out tipo0, op0) ::= %
			{
				tipo0 = mod.lex
				op0 = modulo
			}
			
		Op2 ( out tipo0, op0) ::= and
			{
				tipo0 = logic.lex
				op0 = andLogico
			}

	 * */
	private Token.Tipo op2(Cadena op0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo = tprog.getTipo(), tipo0 = null;
		
		if (valor.equals("*")){
			//operacion0 = "mult";
			tipo0 = tipo;
			op0.setContenido("multiplica");
		}else if (valor.equals("/")){
			//operacion0 = "div";
			tipo0 = tipo;
			op0.setContenido("divide");
		}
		else if (valor.equals("%")){
			//operacion0 = "mod";
			tipo0 = tipo;
			op0.setContenido("modulo");
		}else if (valor.equals("and")){
			//operacion0 = "logic";
			tipo0 = tipo;
			op0.setContenido("andLogico");
		}
		
		return tipo0;
	}
	
	/*
	 * 	Exp3 (out tipo0) ::= 
			{}
			Exp4 (out tipo1) 
				{}
			FExp3 ( out tipo2) 
				{
					tipo0 = errorOperacion3Tipos( tipo1, tipo2 )
				}

	 * */
	private Token.Tipo Exp3() throws ErrorSintacticoExcepcion{
		
		Token.Tipo tipo0 = null, tipo1 = null, tipo2 = null;
		
		tipo1 = Exp4();
		tipo2 = FExp3();
		
		tipo0 = errorOperacion3Tipos(tipo1, tipo2);
		
		//Para ver que hay un error y guardarlo en la tabla
		Token tprog = cogerToken();
		if (tipo0.equals(Token.Tipo.error) && !tipo1.equals(Token.Tipo.error) && !tipo2.equals(Token.Tipo.error))
			tablaErrores.add("Error: en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+ ": Error de operación de tipos de nivel 3 (desplazamientos)");
		
		
		return tipo0;
	}
	
	/*
	 * FExp3 (out tipo0) ::=
		Op3 ( out op1 )
			{}
		Exp3 (out tipo2) 
			{
				tipo0 = tipo2
				emite(op1)
			}
		
		FExp3 ( out tipo0) ::= λ
			{
			tipo0 = λ
			}

	 * */
	private Token.Tipo FExp3() throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo0 = null;
		
		if (esOperador3(valor)){
			Cadena op1 = new Cadena();
			Token.Tipo tipo2 = null;
			op3(op1);
						
			//avanzo indice
			indice++;
			
			tipo2 = Exp3();
			tipo0 = tipo2;
			emite(op1.getContenido());
		}
				
		return tipo0;
					
	}
	
	/*
	 * 
		Op3 ( out op0 ) ::= <<
			{
				op0 = desplazamientoIzq
			}
			
		Op3 ( out op0 ) ::= >>
			{
				op0 = desplazamientoDcha
			}

	 * */
	private void op3(Cadena op0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
				
		if (valor.equals("<<")){
			op0.setContenido("desplazamientoIzq");
		}else if (valor.equals(">>")){
			op0.setContenido("desplazamientoDcha");
		}
	}
	
	/*
	 * 	Exp4 (out tipo0) ::=
			OpAsoc ( out tipo1, op1 )
				{}
			Exp4 ( out tipo2) 
				{
					tipo0 = errorOperacionAsoc4Tipos( tipo2, tipo1 )
					emite(op1)
				}
			
		Exp4 (out tipo0) ::=
			OpNoAsoc ( out tipo1, op1 )
				{}
			Exp5 (out tipo2) 
				{
					tipo0 = errorOperacionNoAsoc4Tipos( tipo2, tipo1 )
					emite(op1)
				}

		Exp4 (out tipo0) ::= 
			{}
			Exp5 (out tipo1) 
				{
					tipo0 =tipo1
				}

	 * */
	private Token.Tipo Exp4() throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo0 = null;
		
		//Si es un operador que asocie
		if (esOperadorAsoc(valor)){
			Token.Tipo tipo1 = null, tipo2 = null;
			Cadena op1 = new Cadena();
			tipo1 = opAsoc(op1);
						
			//avanzo indice
			indice++;
			//Llamada recursiva a Exp4
			tipo2 = Exp4();
			tipo0 = errorOperacionAsoc4Tipos(tipo2, tipo1);
			
			//Para ver que hay un error y guardarlo en la tabla
			if (tipo0.equals(Token.Tipo.error) && !tipo2.equals(Token.Tipo.error))
				tablaErrores.add("Error: en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": Error de operación de tipos de  nivel 4 (unarios) asociativos (inversiones)");
			
			emite(op1.getContenido());
			
		}
		//Si es un operador que no asocia
		else if(tprog.getValor().equals("(") && esOperadorNoAsoc(indice+1) && tokens.get(indice+2).getValor().equals(")")){
			Cadena op1 = new Cadena();
			Token.Tipo tipo1, tipo2;
		
			tipo1 = opNoAsoc(op1);
			
			tipo2 = Exp5();
			tipo0 = errorOperacionNoAsoc4Tipos(tipo2, tipo1);
			
			//Para ver que hay un error y guardarlo en la tabla
			if (tipo0.equals(Token.Tipo.error) && !tipo2.equals(Token.Tipo.error))
				tablaErrores.add("Error: en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": Error de operación de tipos de  4 (unarios)  no asociativos (castings)");
			
			
			emite(op1.getContenido());
		}
		//Si es otro operador
		else if(esOperador0(valor) || esOperador1(valor) || esOperador2(valor) || esOperador3(valor)){
			throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+": El operador debe ser '(float)', '(int)', '(natural)', '(char)', '-' o 'not.");
		}
		//Si es vacio
		else{
			Token.Tipo tipo1;
			tipo1 = Exp5 (); 
			tipo0 = tipo1;
			
		}
		
		return tipo0;
	}
	
	/*
	 * 	OpNoAsoc (out tipo0, op0 ) ::= ( float )
			{
				tipo0 = float.lex
				op0 = castFloat
			}
			
		OpNoAsoc (out tipo0, op0 ) ::= ( int )
			{
				tipo0 = integer.lex
				op0 = castInt
			}
			
		OpNoAsoc (out tipo0, op0 ) ::= ( nat )
			{
				operacion0 = natural.lex
				op0 = castNat
			}
			
		OpNoAsoc (out tipo0, op0 )::= ( char )
			{
				tipo0 = character.lex
				op0 = castChar
			}

	 * */
	private Token.Tipo opNoAsoc(Cadena op0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor =  tprog.getValor();
		
		//Compruebo que se abra el casting
		compruebaValor("(",": se esperaba '('",tprog);
			
		//avanzo indice
		indice++;
		tprog=cogerToken();
		valor = tprog.getValor();
		Token.Tipo tipo0 = null;
		
		//Compruebo el operador de casting
		if (valor.equals("float")){
			tipo0 = Token.Tipo.real;
			op0.setContenido("castFloat");
		}else if (valor.equals("int")){
			tipo0 = Token.Tipo.integer;
			op0.setContenido("castInt");
		}else if (valor.equals("nat")){
			tipo0 = Token.Tipo.natural;
			op0.setContenido("castNatural");
		}else if (valor.equals("char")){
			tipo0 = Token.Tipo.caracter;
			op0.setContenido("castChar");
		}
			
		//avanzo indice
		indice++;
		
		tprog = cogerToken();
		//Compruebo que se cierre el casting
		compruebaValor(")",": se esperaba ')'",tprog);
		
		//avanzo indice
		indice++;
		
		return tipo0;
	}
	
	/*
	 * 	OpAsoc (out tipo0, op0 ) ::= -
			{
				tipo0 = rest.lex
				op0 = notLogico
			}
			
		OpAsoc ( in ; out tipo0, op0 ) ::= not
			{
				tipo0 = logic.lex
				op0 = menosUnario
			}

	 * */
	private Token.Tipo opAsoc(Cadena op0) throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo = tprog.getTipo(), tipo0 = null;
		
		if (valor.equals("-")){
			//operacion0 = "rest";
			tipo0 = tipo;
			op0.setContenido("menosUnario");
		}else if (valor.equals("not")){
			//operacion0 = "logic";
			tipo0 = tipo;
			op0.setContenido("notLogico");
		}
		return tipo0;
		
	}
	
	/*
	 * 	Exp5 (out tipo0) ::= ( 
			{}
			Exp0 (out tipo1)
				{
					tipo0 = tipo1
				}
			)
			
		Exp5 ( out tipo0) ::= real
			{
				tipo0 = float.lex
				emite(cambiaTipo(float.lex));
				emite(apila(real.lex))
			}
			
		Exp5 ( out tipo0) ::= natural
			{
				tipo0 = natural.lex
				emite(cambiaTipo(natural.lex));
				emite(apila(natural.lex))
			}
			
		Exp5 ( out tipo0) ::= carácter
			{
				tipo0 = character.lex
				emite(cambiaTipo(character.lex));
				emite(apila( caracter.lex ))
			}
			
		Exp5 ( out tipo0) ::= true
			{
				tipo0 = boolean.lex
				emite(cambiaTipo(boolean.lex));
				emite(apila(true.lex))
			}
			
		Exp5 ( out tipo0) ::= false
			{
				tipo0 = boolean.lex
				emite(cambiaTipo(boolean.lex));
				emite(apila(false.lex))
			}
			
		Exp5 (out tipo0) ::= identificador
			{
				tipo0 = dameTipo(tsh0, identificador.lex)
				emite(cambiaTipo(dameTipo(tsh0, identificador.lex)));
				emite(apilaVar(dameDireccion( ts, identificador.lex ) ))
			}

	 * */
	private Token.Tipo Exp5() throws ErrorSintacticoExcepcion{
		
		Token tprog=cogerToken();
		String valor = tprog.getValor();
		Token.Tipo tipo = tprog.getTipo(), tipo0 = null;
		
		if (valor.equals("(")){
			//avanzo indice
			indice++;
			
			Token.Tipo tipo1 = null;
			tipo1 = Exp0();
			
			tipo0 = tipo1;
			
			tprog=cogerToken();
			
			//Compruebo que se cierre el parentesis
			compruebaValor(")",": se esperaba ')'",tprog);
			
			//avanzo indice
			indice++;
		}
		else if (tipo == null){
			throw new ErrorSintacticoExcepcion("Error en la línea "+tprog.getFila()+
					", palabra "+tprog.getColumna()+": Debe haber '(' o un valor real, natural, caracter o booleano.");
		}
		else if (tipo.equals(Token.Tipo.identificador)) {
			Token.Tipo tipoError = null;
			emite("cambiaTipo("+ts.dameTipo(valor)+")");
			emite("apilaVar("+ts.dameDireccion(valor)+")");
			tipoError = ts.dameTipo(valor);
			
			if (tipoError.equals(Token.Tipo.error)){
				tablaErrores.add("Error en la línea "+tprog.getFila()+
						", palabra "+tprog.getColumna()+ ": "+tprog.getValor()+" no ha sido declarado anteriormente.");
			}
			tipo0 = tipoError;
			//avanzo indice
			indice++;
		}
		else if (tipo.equals(Token.Tipo.real) || tipo.equals(Token.Tipo.natural) || tipo.equals(Token.Tipo.caracter) || tipo.equals(Token.Tipo.booleano)) {
			emite("cambiaTipo("+tipo+")");
			emite("apila("+valor+")");
			tipo0 = tipo;
			//avanzo indice
			indice++;
		}
		
		return tipo0;
		
	}
	
	//Devuelve si el token es un tipo del que se puede definir
	private Boolean esTipoDec(Token token){
		
		String valor = token.getValor(); 
		return (valor.equals("float") || valor.equals("integer") || valor.equals("natural") 
				|| valor.equals("character") || valor.equals("boolean"));
				
	}
	
	//Devuelve si el token es un valor
	private Boolean esValorDec(Token token){
			
		Token.Tipo tipo = token.getTipo(); 
		return (tipo.equals(Token.Tipo.real) || tipo.equals(Token.Tipo.natural) || tipo.equals(Token.Tipo.caracter) 
				|| tipo.equals(Token.Tipo.booleano));
					
	}
	
	//Devuelve true si el valor es uno de operacion0
	private Boolean esOperador0(String valor){
		
		return (valor.equals("<") || valor.equals(">") || valor.equals("<=") 
				|| valor.equals(">=") || valor.equals("==")  || valor.equals("!=")); 
	}
	
	//Devuelve true si el valor es uno de operacion1
	private Boolean esOperador1(String valor){
		
		return (valor.equals("+") || valor.equals("-") || valor.equals("or")); 
	}

	//Devuelve true si el valor es uno de operacion2
	private Boolean esOperador2(String valor){
		
		return (valor.equals("*") || valor.equals("/") || valor.equals("%") 
				|| valor.equals("and"));
	}
	
	//Devuelve true si el valor es uno de operacion3
	private Boolean esOperador3(String valor){
		
		return (valor.equals("<<") || valor.equals(">>"));
	}
	
	//Devuelve true si el valor es uno de opNoAsig
	private Boolean esOperadorNoAsoc(int posicion){
		
		String valor = tokens.get(posicion).getValor();
		return (valor.equals("float") || valor.equals("int") || valor.equals("nat") || valor.equals("char"));
	}
		
	//Devuelve true si el valor es uno de opNoAsig
	private Boolean esOperadorAsoc(String valor){
		
		return (valor.equals("-") || valor.equals("not"));
	}
	
	//Crea la tabla de simbolos vacía
	private TablaSimbolos creaTablaSimbolos(){
		
		return new TablaSimbolos();
	}
	
	//Mete una instrucción en la siguiente posición del código
	private void emite(String instruccion){
		
		//meto la instrucción si no es vacía
		if (!instruccion.equals(""))
			cod.add(instruccion);
	}
	
	//Comprueba el valor de un token
	private void compruebaValor(String valor,String comentario,Token token) throws ErrorSintacticoExcepcion{
		
		//Compruebo que el siguiente token sea ")"
		if (!token.getValor().equals(valor)){
			throw new ErrorSintacticoExcepcion("Error en la línea "+token.getFila()+", palabra "+token.getColumna()+comentario);
		}
	}
	
	//Comprueba el tipo de un token
	private void compruebaTipo(Token.Tipo tipo,String comentario,Token token) throws ErrorSintacticoExcepcion{
		
		//Compruebo que el siguiente token sea ")"
		if (token.getTipo()==null || !token.getTipo().equals(tipo)){
			throw new ErrorSintacticoExcepcion("Error en la línea "+token.getFila()+", palabra "+token.getColumna()+comentario);
		}
	}
		
	//Funcion que hace una conversión de valor a tipo (String a Token.Tipo)
	private Token.Tipo convierteValorATipo(Cadena valor){
		Token.Tipo tipo = null;
			
		if (valor.getContenido().equals("float")){
			tipo = Token.Tipo.real;
		}
		else if (valor.getContenido().equals("integer")){
			tipo = Token.Tipo.integer;
		}
		else if (valor.getContenido().equals("natural")){
			tipo = Token.Tipo.natural;
		}
		else if (valor.getContenido().equals("character")){
			tipo = Token.Tipo.caracter;
		}
		else if (valor.getContenido().equals("boolean")){
			tipo = Token.Tipo.booleano;
		}
		
		return tipo;
	}
	
	//**************************************  FUNCIONES DE RESTRICCIONES CONTEXTUALES  ************************************************//
	
	//Mira si hay error de asignacion de tipos
	private boolean errorAsignacionTipos(Token.Tipo tipo1 ,Token.Tipo tipo2,Token.Tipo op){
				
		//Si no hay operación
		if (op == null){
			//Si Tipo1 es boolean o caracter
			if (tipo1.equals(Token.Tipo.booleano) || tipo1.equals(Token.Tipo.caracter))
				return !tipo1.equals(tipo2);
			//Si tipo1 es real
			else if (tipo1.equals(Token.Tipo.real))
				return (tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano));
			//Si tipo1 es entero
			else if (tipo1.equals(Token.Tipo.integer))
				return (tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano) || tipo2.equals(Token.Tipo.real));
			//Si tipo1 es natural
			else if (tipo1.equals(Token.Tipo.natural))
				return (!tipo2.equals(Token.Tipo.natural));
		}
		//Si es una suma
		else if (op.equals(Token.Tipo.sum)){
			//Si Tipo1 es boolean o caracter
			if (tipo1.equals(Token.Tipo.booleano) || tipo1.equals(Token.Tipo.caracter))
				return true;
			//Si tipo1 es real
			else if (tipo1.equals(Token.Tipo.real))
				return (tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano));
			//Si tipo1 es entero
			else if (tipo1.equals(Token.Tipo.integer))
				return (tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano) || tipo2.equals(Token.Tipo.real));
			//Si tipo1 es natural
			else if (tipo1.equals(Token.Tipo.natural))
				return (!tipo2.equals(Token.Tipo.natural));			
		}
		//Si es una resta
		else if (op.equals(Token.Tipo.rest)){
			//Si es booleano o carácter o natural
			if (tipo1.equals(Token.Tipo.booleano) || tipo1.equals(Token.Tipo.caracter) || tipo1.equals(Token.Tipo.natural))
				return true;
			//Si es real
			else if (tipo1.equals(Token.Tipo.real))
				return (tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano));
			//Si es entero
			else if (tipo1.equals(Token.Tipo.integer))
				return (tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano) || tipo2.equals(Token.Tipo.real));
		}
				
		return true;
	}
	
	/*Para los operadores de nivel 0 (relacionales), 
	 * comprueba que los tipos a ambos lados sean los adecuados. 
	 * Devuelve el tipo de operacion si no hay error
	 */
	private Token.Tipo errorOperacion0Tipos(Token.Tipo tipo1,Token.Tipo tipo2){
						
		if (tipo2 != null){
			//Si ya habia errores devuelvo error
			if (tipo1.equals(Token.Tipo.error) || tipo2.equals(Token.Tipo.error))
				return Token.Tipo.error;
			//Si tipo1 es caracter o booleano
			else if (tipo1.equals(Token.Tipo.caracter) || tipo1.equals(Token.Tipo.booleano)){
				if (!tipo1.equals(tipo2))
					return Token.Tipo.error;
				else return Token.Tipo.booleano;
			}
			//Si tipo1 es un número, tipo2 tiene que ser un número
			else if (tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano))
				return Token.Tipo.error;
			
			return Token.Tipo.booleano;
		}
		else return tipo1;
	}
	
	/*Para los operadores de nivel 1 (aritmético-lógicas de menor prioridad), 
	 * comprueba que los tipos a ambos lados sean los adecuados. 
	 * Devuelve el tipo de operacion si no hay error
	 */
	private Token.Tipo errorOperacion1Tipos(Token.Tipo tipo1, Token.Tipo tipo2, Token.Tipo op){
	
		//Si ya habia errores devuelvo error
		if (tipo1.equals(Token.Tipo.error) || tipo2.equals(Token.Tipo.error))
			return Token.Tipo.error;
		//Si el operador es logico
		else if (op.equals(Token.Tipo.logic)){
			//Si son booleanos los tipos
			if (tipo1.equals(Token.Tipo.booleano) && tipo2.equals(Token.Tipo.booleano))
				return Token.Tipo.booleano;
			else return Token.Tipo.error;
		}
		//Aquí ya sabemos que es una suma o resta
		else if (tipo1.equals(Token.Tipo.caracter) || tipo1.equals(Token.Tipo.booleano) || tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano))
			return Token.Tipo.error;
		//Aquí ya sabemos que ambos tipos son numéricos
		else if (tipo1.equals(Token.Tipo.real) || tipo2.equals(Token.Tipo.real))
			return Token.Tipo.real;
		else if (tipo1.equals(Token.Tipo.integer) || tipo2.equals(Token.Tipo.integer))
			return Token.Tipo.integer;
			
		return Token.Tipo.natural;
	}
	
	/*Para los operadores de nivel 2 (aritméticos de mayor prioridad), 
	 * comprueba que los tipos a ambos lados sean los adecuados. 
	 * Devuelve el tipo de operacion si no hay error
	 */
	private Token.Tipo errorOperacion2Tipos(Token.Tipo tipo1, Token.Tipo tipo2, Token.Tipo op){
	
		//Si ya habia errores devuelvo error
		if (tipo1.equals(Token.Tipo.error) || tipo2.equals(Token.Tipo.error))
			return Token.Tipo.error;
		//Si es una operación lógica
		else if (op.equals(Token.Tipo.logic)){
			//Si son booleanos los tipos
			if (tipo1.equals(Token.Tipo.booleano) && tipo2.equals(Token.Tipo.booleano))
				return Token.Tipo.booleano;
			else return Token.Tipo.error;
		}
		//Aquí ya sabemos que es una multiplicación, división o módulo
		else if (tipo1.equals(Token.Tipo.caracter) || tipo1.equals(Token.Tipo.booleano) || tipo2.equals(Token.Tipo.caracter) || tipo2.equals(Token.Tipo.booleano))
			return Token.Tipo.error;
		//Aquí ya sabemos que ambos tipos son numéricos
		else if (op.equals(Token.Tipo.mult)){
			if ( tipo1.equals(Token.Tipo.real) || tipo2.equals(Token.Tipo.real))
				return Token.Tipo.real;
			else if ( tipo1.equals(Token.Tipo.integer) || tipo2.equals(Token.Tipo.integer))
				return Token.Tipo.integer;
			else return Token.Tipo.natural;
		}
		else if (op.equals(Token.Tipo.div)){
			if ( tipo1.equals(Token.Tipo.real) || tipo2.equals(Token.Tipo.real))
				return Token.Tipo.real;
			else if ( tipo1.equals(Token.Tipo.integer) || tipo2.equals(Token.Tipo.integer))
				return Token.Tipo.integer;
			else return Token.Tipo.natural;
		}
		//Aquí ya sabemos que se trata de la operación módulo
		else if (tipo1.equals(Token.Tipo.real) || !tipo2.equals(Token.Tipo.natural))
			return Token.Tipo.error;
		
		return tipo1;
	}
	
	/*Para los operadores de nivel 3 (desplazamientos), 
	 * comprueba que los tipos a ambos lados sean los adecuados. 
	 * Devuelve el tipo de operacion si no hay error
	 */
	private Token.Tipo errorOperacion3Tipos(Token.Tipo tipo1, Token.Tipo tipo2){
		
		//Si ya habia errores devuelvo error
		if (tipo2 != null)
		{
			if (tipo1.equals(Token.Tipo.error) || tipo2.equals(Token.Tipo.error))
				return Token.Tipo.error;
			else if (!tipo1.equals(Token.Tipo.natural) || !tipo2.equals(Token.Tipo.natural))
				return Token.Tipo.error;
				
			return Token.Tipo.natural;
		}
		else return tipo1;
	}
	
	/*Para los operadores de nivel 4 (unarios) asociativos (inversiones), 
	 * comprueba que los tipos a ambos lados sean los adecuados. 
	 * Devuelve el tipo de operacion si no hay error
	 */
	private Token.Tipo 	errorOperacionAsoc4Tipos(Token.Tipo tipo1, Token.Tipo op){
			
		//Si ya habia errores devuelvo error
		if (tipo1.equals(Token.Tipo.error))
			return Token.Tipo.error;
		//Si el operador es -
		else if (op.equals(Token.Tipo.rest)){
			if (tipo1.equals(Token.Tipo.booleano) || tipo1.equals(Token.Tipo.caracter))
				return Token.Tipo.error;
			else if (tipo1.equals(Token.Tipo.natural))
				return Token.Tipo.integer;
			else return tipo1;
		}
		// Aquí ya sabemos que se trata del operador lógico not
		else if (!tipo1.equals(Token.Tipo.booleano))
			return Token.Tipo.error;
		
		return Token.Tipo.booleano;
	}
	
	/*Para los operadores de nivel 4 (unarios)  no asociativos (castings), 
	 * comprueba que los tipos a ambos lados sean los adecuados. 
	 * Devuelve el tipo de operacion si no hay error
	 */
	private Token.Tipo 	errorOperacionNoAsoc4Tipos(Token.Tipo tipo1, Token.Tipo op){
			
		//Si ya habia errores devuelvo error
		if (tipo1.equals(Token.Tipo.error))
			return Token.Tipo.error;
		//Si es real
		else if (op.equals(Token.Tipo.real)){
			if (tipo1.equals(Token.Tipo.booleano))
				return Token.Tipo.error;
			else return Token.Tipo.real;
		}
		//Si es entero
		else if (op.equals(Token.Tipo.integer)){
			if (tipo1.equals(Token.Tipo.booleano))
				return Token.Tipo.error;
			else return Token.Tipo.integer;
		}
		//Si es natural
		else if (op.equals(Token.Tipo.natural)){
			if (tipo1.equals(Token.Tipo.booleano) || tipo1.equals(Token.Tipo.real) || tipo1.equals(Token.Tipo.integer))
				return Token.Tipo.error;
			else return Token.Tipo.natural;
		}
		// Aquí ya sabemos que se trata del casting de char
		else if (tipo1.equals(Token.Tipo.booleano) || tipo1.equals(Token.Tipo.real) || tipo1.equals(Token.Tipo.integer))
			return Token.Tipo.error;
		
		return Token.Tipo.caracter;
	}
	
	private Token cogerToken() throws ErrorSintacticoExcepcion{
		
		if (tokens.size()>indice){
			return tokens.get(indice);
		}
		else{
			throw new ErrorSintacticoExcepcion("Error en la última línea, no puede finalizar de esa forma el programa.");
		}
	}
}